
package com.sz.biz.logistics.ord.service.impl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.sz.biz.common.constants.ErrorCodes;
import com.sz.biz.common.customer.entity.CusCustomer;
import com.sz.biz.common.customer.service.CustomerService;
import com.sz.biz.common.edi.service.SynClassCargo;
import com.sz.biz.common.edi.service.SynClassCargoService;
import com.sz.biz.logistics.base.entity.*;
import com.sz.biz.logistics.base.service.*;
import com.sz.biz.logistics.billing.dto.QuotationCalcObject;
import com.sz.biz.logistics.billing.dto.QuotationCalcResult;
import com.sz.biz.logistics.billing.service.QuotationCalcService;
import com.sz.biz.logistics.serialno.entity.SerialNoManagement;
import com.sz.biz.logistics.serialno.service.NoManagementService;
import com.sz.biz.logistics.constants.SubServiceTypes;
import com.sz.biz.logistics.constants.TopServiceTypes;
import com.sz.biz.logistics.core.constants.ProductServiceRegionConstants;
import com.sz.biz.logistics.core.dto.ProductServiceRelCargotypeInfo;
import com.sz.biz.logistics.core.entity.*;
import com.sz.biz.logistics.core.service.*;
import com.sz.biz.logistics.ord.dto.OrdOrderOptionService;
import com.sz.biz.logistics.ord.dto.OrdServiceAssignCalcDto;
import com.sz.biz.logistics.ord.entity.*;
import com.sz.biz.logistics.ord.service.*;
import com.sz.common.base.dao.ParamData;
import com.sz.common.base.exception.BizException;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.service.AbstractService;
import com.sz.common.base.utils.DateUtils;
import com.sz.common.base.utils.EntityUtils;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.system.service.UnitConversionService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.*;

/**
 * Function: 订单的服务分配线程. <br>
 * Date: 2017年10月18日 17:07 <br>
 * Author: wentao.chang
 */
@org.springframework.stereotype.Service
public class OrdOrderServiceAssignServiceImpl extends AbstractService implements OrdOrderServiceAssignService {

    private static String ADDRESSSPLIT = "\\|";
    private static String ABOVE = "above";
    private static String BELOW = "below";
    private static int RETRYCHANNELCOUNT = 10;

    @Autowired
    private OrdOrderService orderService;
    @Autowired
    private OrdOrderAddressService ordOrderAddressService;
    @Autowired
    private OrdOrderCargoService ordOrderCargoService;
    @Autowired
    private OrdOrderAdditionalService ordOrderAdditionalService;

    @Autowired
    private CustomerService customerService;

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductServiceRelService productServiceRelService;
    @Autowired
    private ProductServiceCargotypeRelService productServiceCargotypeRelService;

    @Autowired
    private QuotationCalcService quotationCalcService;

    @Autowired
    private ServiceService serviceService;

    @Autowired
    private ServiceTypeRelService serviceTypeRelService;

    @Autowired
    private AirlineDetailService airlineDetailService;
    @Autowired
    private ShippingLineDetailService shippingLineDetailService;
    @Autowired
    private ServiceEffectivenessService serviceEffectivenessService;
    @Autowired
    private SynClassCargoService synClassCargoService;
    @Autowired
    private FlightService flightService;
    @Autowired
    private ShippingLineSettingService shippingLineSettingService;

    @Autowired
    private UnitConversionService unitConversionService;


    @Autowired
    private AirportService airportService;


    @Autowired
    private PortService portService;


    @Autowired
    private OrdOrderService ordOrderService;
    @Autowired
    private OrdTrackingMessageService trackingMessageService;

    @Autowired
    private NoManagementService noManagementService;


    /**
     * 创建一个线程池
     */
    private ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("osa-pool-%d").build();

    /**
     * Common Thread Pool
     */
    private ExecutorService pool = new ThreadPoolExecutor(5, 200,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());


    /**
     * cron 定时任务调用
     */
    @Override
    public void cron() {
        //获取计算的运单
        List<OrdOrder> orders = ordOrderService.findNoAssignServiceBills();
        if (!CollectionUtils.isEmpty(orders)) {

            orders.forEach(item -> {

                ProductServiceRel productServiceRel = trackingMessageService
                        .findCurServiceByTracking(item.getWaybillNo(), item.getProductUid(), item.getCreateTime());
                String serviceTypeCode = "";
                if (!com.sz.common.base.utils.ObjectUtils.isEmpty(productServiceRel)) {
                    serviceTypeCode = productServiceRel.getServiceTypeCode();
                }
                this.submitBill(item.getOrderNo(), serviceTypeCode);
            });
        }

    }

    /**
     * 提交计算的运单号
     *
     * @param orderNo         运单号
     * @param serviceTypeCode 当前订单进行到的阶段
     */
    @Override
    public void submitBill(String orderNo, String serviceTypeCode) {
        OrderAssignServiceRunnable command = new OrderAssignServiceRunnable(orderNo, serviceTypeCode);
        pool.execute(command);
    }


    private class OrderAssignServiceRunnable implements Runnable {
        private String orderNo;
        private String serviceTypeCode;

        OrderAssignServiceRunnable(String orderNo, String serviceTypeCode) {
            this.orderNo = orderNo;
            this.serviceTypeCode = serviceTypeCode;
        }

        @Override
        public void run() {
            calc(orderNo, serviceTypeCode);
        }
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void calc(String orderNo, String skipServiceTypeCodeFrom) {
        OrdServiceAssign assign = new OrdServiceAssign();
        assign.setOrderNo(orderNo);
        assign.setCalcTime(new Date());

        OrdServiceAssignCalcDto calcDto = new OrdServiceAssignCalcDto();
        calcDto.setOrderNo(orderNo);

        //获取订单
        OrdOrder ord = orderService.findByOrderNo(calcDto.getOrderNo());
        calcDto.setCargoType(ord.getCargoType());
        calcDto.setCusWeight(ord.getCusWeight());
        calcDto.setWeightUnit(ord.getCusWeightUnit());
        calcDto.setCustId(ord.getCustomerId());
        calcDto.setOrderDate(ord.getCreateTime());

        //获取客户的评价等级
        CusCustomer cusCustomer = customerService.findShortById(ord.getCustomerId());
        if (cusCustomer.getEvaluateLeval() != null) {
            calcDto.setCusLevel(cusCustomer.getEvaluateLeval());
        } else {
            calcDto.setCusLevel(0);
        }

        OrdOrderAddress ordOrderAddress = ordOrderAddressService.findByOrderNo(calcDto.getOrderNo());
        StringBuilder sb = new StringBuilder();

        boolean isStartCn = ordOrderAddress.getFromCountry().contains("CN");
        sb.append(isStartCn ? 2 : 3);
        sb.append("-").append(ordOrderAddress.getFromCountry());
        if (isStartCn) {
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFromProvince()) ? " " : ordOrderAddress.getFromProvince());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFromCity()) ? " " : ordOrderAddress.getFromCity());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFromDistrict()) ? " " : ordOrderAddress.getFromDistrict());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFromStreet()) ? " " : ordOrderAddress.getFromStreet());
        } else {
            sb.append("-").append(" ");
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFromCity()) ? " " : ordOrderAddress.getFromCity());
            sb.append("-").append(" ");
            sb.append("-").append(" ");
        }
        calcDto.setFromCountry(ordOrderAddress.getFromCountry());
        calcDto.setFromProvince(ordOrderAddress.getFromProvince());
        calcDto.setFromCity(ordOrderAddress.getFromCity());
        calcDto.setFromStreet(ordOrderAddress.getFromStreet());
        calcDto.setFromArea(ordOrderAddress.getFromAddress());
        calcDto.setFromPostcode(ordOrderAddress.getFromPostcode());
        calcDto.setStartAddressStr(sb.toString());


        sb.setLength(0);
        boolean isToCn = ordOrderAddress.getToCountry().contains("CN");
        sb.append(isToCn ? 2 : 3);
        sb.append("-").append(ordOrderAddress.getToCountry());
        if (isStartCn) {
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getToProvince()) ? " " : ordOrderAddress.getToProvince());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getToCity()) ? " " : ordOrderAddress.getToCity());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getToDistrict()) ? " " : ordOrderAddress.getToDistrict());
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getToStreet()) ? " " : ordOrderAddress.getToStreet());

        } else {
            sb.append("-").append(" ");
            sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getToCity()) ? " " : ordOrderAddress.getToCity());
            sb.append("-").append(" ");
            sb.append("-").append(" ");
        }
        calcDto.setToCountry(ordOrderAddress.getToCountry());
        calcDto.setToProvince(ordOrderAddress.getToProvince());
        calcDto.setToCity(ordOrderAddress.getToCity());
        calcDto.setToStreet(ordOrderAddress.getToStreet());
        calcDto.setToArea(ordOrderAddress.getToAddress());
        calcDto.setToPostcode(ordOrderAddress.getToPostcode());
        calcDto.setEndAddressStr(sb.toString());

        sb.setLength(0);
        if (ordOrderAddress.getFetchCountry() != null) {
            boolean isFetchCn = ordOrderAddress.getFetchCountry().contains("CN");
            sb.append(isFetchCn ? 2 : 3);
            sb.append("-").append(ordOrderAddress.getFetchCountry());
            if (isFetchCn) {
                sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFetchProvince()) ? " " : ordOrderAddress.getFetchProvince());
                sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFetchCity()) ? " " : ordOrderAddress.getFetchCity());
                sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFetchDistrict()) ? " " : ordOrderAddress.getFetchDistrict());
                sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFetchStreet()) ? " " : ordOrderAddress.getFetchStreet());

            } else {
                sb.append("-").append(" ");
                sb.append("-").append(StringUtils.isEmpty(ordOrderAddress.getFetchCity()) ? " " : ordOrderAddress.getFetchCity());
                sb.append("-").append(" ");
                sb.append("-").append(" ");
            }

            calcDto.setReceiveAddressStr(sb.toString());
        }

        calcDto.setStartPort(ordOrderAddress.getFromTransportId());
        calcDto.setEndPort(ordOrderAddress.getToTransportId());

        List<OrdOrderAdditional> oods = ordOrderAdditionalService.findByOrderNo(calcDto.getOrderNo());

        sb.setLength(0);
        if (!CollectionUtils.isEmpty(oods)) {
            oods.forEach(item -> {
                if (!calcDto.getOptionServiceTypes().contains(item.getServiceTypeCode())) {
                    calcDto.getOptionServiceTypes().add(item.getServiceTypeCode());
                }
            });
        }

        //获取货物明细的总价格
        List<OrdOrderCargo> cargos = ordOrderCargoService.findByOrderNo(calcDto.getOrderNo());
        if (!CollectionUtils.isEmpty(cargos)) {
            cargos.forEach(cargo -> {
                BigDecimal cargoAmount = cargo.getGoodsPrice().multiply(new BigDecimal(cargo.getGoodsNumber()));
                calcDto.setAnnouncePrice(calcDto.getAnnouncePrice().add(cargoAmount));
            });
        }

        putMsg(calcDto, "组装成功需要匹配的对象:");
        putMsg(calcDto, "当前时间:" + DateUtils.formatDetailDate(assign.getCalcTime()));
        putMsg(calcDto, "单号:" + calcDto.getOrderNo());
        putMsg(calcDto, "订单时间:" + DateUtils.formatDetailDate(calcDto.getOrderDate()));
        putMsg(calcDto, "客户id:" + calcDto.getCustId());
        putMsg(calcDto, "客户等级:" + calcDto.getCusLevel());

        putMsg(calcDto, "订单货物类型:" + calcDto.getCargoType());
        putMsg(calcDto, "订单重量:" + calcDto.getCusWeight().doubleValue() + calcDto.getWeightUnit());
        putMsg(calcDto, "订单起始地:" + calcDto.getStartAddressStr());
        putMsg(calcDto, "订单起始港:" + calcDto.getStartPort());
        putMsg(calcDto, "订单目的地:" + calcDto.getEndAddressStr());
        putMsg(calcDto, "订单目的港:" + calcDto.getEndPort());
        putMsg(calcDto, "订单收货地:" + calcDto.getReceiveAddressStr());
        putMsg(calcDto, "订单声明价值:" + calcDto.getAnnouncePrice().doubleValue());
        putMsg(calcDto, "订单使用下列可选服务类型:" + StringUtils.toCommaString(calcDto.getOptionServiceTypes()));

        //获取产品
        Product prod = productService.findShortActivedByUid(ord.getProductUid());
        putMsg(calcDto, "获取到下单产品:" + prod.getName() + " id:[" + prod.getId() + "]");

        //已按节点序号排序
        List<ProductServiceRel> prodServices = productServiceRelService.findByProdId(prod.getId());
        Map<String, List<ProductServiceRel>> prodServicesMap = new HashMap<>(16);
        //当前产品所有的服务类型编码(serviceTypeCode)
        List<String> serviceTypes = new ArrayList<>();
        List<String> readables = new ArrayList<>();
        if (!CollectionUtils.isEmpty(prodServices)) {
            Map<String, String> skippedSTs = new HashMap<>(16);
            final boolean[] isStop = {false};
            prodServices.forEach(item -> {
                if (!serviceTypes.contains(item.getServiceTypeCode())) {
                    serviceTypes.add(item.getServiceTypeCode());
                    readables.add(item.getServiceTypeCode() + "[" + item.getServiceProperty().getCode() + "]");
                }
                prodServicesMap.putIfAbsent(item.getServiceTypeCode(), new ArrayList<>());
                prodServicesMap.get(item.getServiceTypeCode()).add(item);

                //在serviceTypeCode服务类型之前的不再重新分派(包含自身也不重分)
                if (!StringUtils.isEmpty(skipServiceTypeCodeFrom) && !isStop[0]) {
                    String st = item.getServiceTypeCode();
                    if (!skippedSTs.containsKey(st)) {
                        calcDto.getSkippedServiceTypes().add(st);
                        skippedSTs.put(st, "");
                    }
                    if (skipServiceTypeCodeFrom.equals(st)) {
                        isStop[0] = true;
                    }
                }
            });
        }
        if (!CollectionUtils.isEmpty(serviceTypes)) {
            assign.setStTypes(StringUtils.toCommaString(readables) + "|" +
                    StringUtils.toCommaString(calcDto.getOptionServiceTypes()));

            putMsg(calcDto, "产品所有的服务类型:" + StringUtils.toCommaString(readables));
            putMsg(calcDto, "将按照产品服务节点的顺序计算\r\n");
            try {
                OrdServiceAssign osaHistory = findByOrderNosWithoutAssign(orderNo);
                //遍历按顺序生成的服务类型
                serviceTypes.forEach(serviceType -> {
                    putMsg(calcDto, "\r\n--------------------" + serviceType + "-----------------");
                    //处理跳过的
                    if (!StringUtils.isEmpty(skipServiceTypeCodeFrom)
                            && calcDto.getSkippedServiceTypes().contains(serviceType)
                            && osaHistory != null) {
                        String uid = "";
                        switch (serviceType) {
                            //揽收 //仓储  //清关 //干线  //配送 //其他
                            case TopServiceTypes.RECEIVE:
                                assign.setSt001Id(osaHistory.getSt001Id());
                                assign.setSt001Uid(osaHistory.getSt001Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt001ChannelNo())) {
                                    assign.setSt001ChannelNo(osaHistory.getSt001ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt001Uid());
                                    assign.setSt001ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt001Uid();
                                break;
                            case TopServiceTypes.STORE:
                                assign.setSt002Id(osaHistory.getSt002Id());
                                assign.setSt002Uid(osaHistory.getSt002Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt002ChannelNo())) {
                                    assign.setSt002ChannelNo(osaHistory.getSt002ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt002Uid());
                                    assign.setSt002ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt002Uid();
                                break;
                            case TopServiceTypes.PORT:
                                assign.setSt003Id(osaHistory.getSt003Id());
                                assign.setSt003Uid(osaHistory.getSt003Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt003ChannelNo())) {
                                    assign.setSt003ChannelNo(osaHistory.getSt003ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt003Uid());
                                    assign.setSt003ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt003Uid();
                                break;
                            case TopServiceTypes.MAINLINE:
                                assign.setSt004Id(osaHistory.getSt004Id());
                                assign.setSt004Uid(osaHistory.getSt004Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt004ChannelNo())) {
                                    assign.setSt004ChannelNo(osaHistory.getSt004ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt004Uid());
                                    assign.setSt004ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt004Uid();
                                break;
                            case TopServiceTypes.DELIVERY:
                                assign.setSt005Id(osaHistory.getSt005Id());
                                assign.setSt005Uid(osaHistory.getSt005Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt005ChannelNo())) {
                                    assign.setSt005ChannelNo(osaHistory.getSt005ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt005Uid());
                                    assign.setSt005ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt005Uid();
                                break;
                            case TopServiceTypes.OTHER:
                                assign.setSt006Id(osaHistory.getSt006Id());
                                assign.setSt006Uid(osaHistory.getSt006Uid());
                                if (StringUtils.isNotEmpty(osaHistory.getSt006ChannelNo())) {
                                    assign.setSt006ChannelNo(osaHistory.getSt006ChannelNo());
                                } else {
                                    String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), osaHistory.getSt006Uid());
                                    assign.setSt006ChannelNo(channelNo);
                                    putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                                }
                                uid = osaHistory.getSt006Uid();
                                break;
                            default:
                                //do nothing
                        }
                        putMsg(calcDto, "\r\n由于运单已在处理中，" + serviceType + "的服务将使用历史的分派：" + uid);
                        return;
                    }
                    List<ProductServiceRel> typedServices = prodServicesMap.get(serviceType);
                    compareAndGetMatchService(serviceType, typedServices, calcDto, assign, osaHistory);

                });
            } catch (BizException e) {
                putMsg(calcDto, "遍历按顺序生成的服务类型,计算失败，原因:" + e.getMsg());
                assign.setIsAssignOk(Boolean.FALSE);
                logger.error(calcDto.getCalcMsg().toString());
            } catch (Exception e) {
                putMsg(calcDto, "遍历按顺序生成的服务类型,计算失败，原因:" + e.getClass() + e.getMessage());
                putMsg(calcDto, StringUtils.arrayToDelimitedString(e.getStackTrace(), "\r\n"));
                assign.setIsAssignOk(Boolean.FALSE);
                logger.error(calcDto.getCalcMsg().toString());
            }


        } else

        {
            putMsg(calcDto, "产品竟然没有筛选到服务,计算失败");
            assign.setIsAssignOk(Boolean.FALSE);
            logger.error(calcDto.getCalcMsg().toString());
        }
        assign.setCalcMsg(calcDto.getCalcMsg().

                toString());
        if (StringUtils.isEmpty(assign.getSt001Uid()) &&
                StringUtils.isEmpty(assign.getSt002Uid()) &&
                StringUtils.isEmpty(assign.getSt003Uid()) &&
                StringUtils.isEmpty(assign.getSt004Uid()) &&
                StringUtils.isEmpty(assign.getSt005Uid()) &&
                StringUtils.isEmpty(assign.getSt006Uid())
                )

        {
            assign.setIsAssignOk(Boolean.FALSE);
        }
        logger.info(calcDto.getCalcMsg().

                toString());
        this.

                insert(assign);

    }

    @Override
    public OrdServiceAssign calcForTry(QuotationCalcObject calcObject) {
        OrdServiceAssign assign = new OrdServiceAssign();
        assign.setOrderNo(null);
        assign.setCalcTime(new Date());

        OrdServiceAssignCalcDto calcDto = new OrdServiceAssignCalcDto();
        calcDto.setOrderNo(null);

        //构造订单
        calcDto.setCargoType(calcObject.getCargoTypeCode());
        calcDto.setCusWeight(calcObject.getWeight());
        calcDto.setWeightUnit(calcObject.getWeightUnit());
        calcDto.setCustId(calcObject.getCustomerId());
        calcDto.setOrderDate(new Date());

        //获取客户的评价等级
        if (calcObject.getCustomerId() != 0) {
            CusCustomer cusCustomer = customerService.findShortById(calcObject.getCustomerId());
            if (cusCustomer.getEvaluateLeval() != null) {
                calcDto.setCusLevel(cusCustomer.getEvaluateLeval());
            }
        }

        StringBuilder sb = new StringBuilder();

        boolean isStartCn = calcObject.getFromCountry().contains("CN");
        sb.append(isStartCn ? 2 : 3);
        sb.append("-").append(calcObject.getFromCountry());
        if (isStartCn) {
            sb.append("-").append(StringUtils.isEmpty(calcObject.getFromProvince()) ? " " : calcObject.getFromProvince());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getFromCity()) ? " " : calcObject.getFromCity());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getFromArea()) ? " " : calcObject.getFromArea());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getFromStreet()) ? " " : calcObject.getFromStreet());
        } else {
            sb.append("-").append(" ");
            sb.append("-").append(StringUtils.isEmpty(calcObject.getFromCity()) ? " " : calcObject.getFromCity());
            sb.append("-").append(" ");
            sb.append("-").append(" ");
        }
        calcDto.setFromCountry(calcObject.getFromCountry());
        calcDto.setFromProvince(calcObject.getFromProvince());
        calcDto.setFromCity(calcObject.getFromCity());
        calcDto.setFromStreet(calcObject.getFromStreet());
        calcDto.setFromArea(calcObject.getFromArea());
        calcDto.setFromPostcode(calcObject.getFromPostcode());
        calcDto.setStartAddressStr(sb.toString());


        sb.setLength(0);
        boolean isToCn = calcObject.getToCountry().contains("CN");
        sb.append(isToCn ? 2 : 3);
        sb.append("-").append(calcObject.getToCountry());
        if (isStartCn) {
            sb.append("-").append(StringUtils.isEmpty(calcObject.getToProvince()) ? " " : calcObject.getToProvince());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getToCity()) ? " " : calcObject.getToCity());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getToArea()) ? " " : calcObject.getToArea());
            sb.append("-").append(StringUtils.isEmpty(calcObject.getToStreet()) ? " " : calcObject.getToStreet());

        } else {
            sb.append("-").append(" ");
            sb.append("-").append(StringUtils.isEmpty(calcObject.getToCity()) ? " " : calcObject.getToCity());
            sb.append("-").append(" ");
            sb.append("-").append(" ");
        }
        calcDto.setToCountry(calcObject.getToCountry());
        calcDto.setToProvince(calcObject.getToProvince());
        calcDto.setToCity(calcObject.getToCity());
        calcDto.setToStreet(calcObject.getToStreet());
        calcDto.setToArea(calcObject.getToArea());
        calcDto.setToPostcode(calcObject.getToPostcode());
        calcDto.setEndAddressStr(sb.toString());

        sb.setLength(0);
        if (calcObject.getFetchCountry() != null) {
            boolean isFetchCn = calcObject.getFetchCountry().contains("CN");
            sb.append(isFetchCn ? 2 : 3);
            sb.append("-").append(calcObject.getFetchCountry());
            if (isFetchCn) {
                sb.append("-").append(StringUtils.isEmpty(calcObject.getFetchProvince()) ? " " : calcObject.getFetchProvince());
                sb.append("-").append(StringUtils.isEmpty(calcObject.getFetchCity()) ? " " : calcObject.getFetchCity());
                sb.append("-").append(StringUtils.isEmpty(calcObject.getFetchArea()) ? " " : calcObject.getFetchArea());
                sb.append("-").append(StringUtils.isEmpty(calcObject.getFetchStreet()) ? " " : calcObject.getFetchStreet());

            } else {
                sb.append("-").append(" ");
                sb.append("-").append(StringUtils.isEmpty(calcObject.getFetchCity()) ? " " : calcObject.getFetchCity());
                sb.append("-").append(" ");
                sb.append("-").append(" ");
            }

            calcDto.setReceiveAddressStr(sb.toString());
        }

        calcDto.setStartPort(calcObject.getStartPort());
        calcDto.setEndPort(calcObject.getEndPort());

        calcDto.setAnnouncePrice(calcObject.getAnnouncePrice());

        putMsg(calcDto, "组装成功需要匹配的对象:");
        putMsg(calcDto, "当前时间:" + DateUtils.formatDetailDate(assign.getCalcTime()));
        putMsg(calcDto, "单号:模拟");
        putMsg(calcDto, "模拟订单时间:" + DateUtils.formatDetailDate(calcDto.getOrderDate()));
        putMsg(calcDto, "客户id:" + calcDto.getCustId());
        putMsg(calcDto, "客户等级:" + calcDto.getCusLevel());

        putMsg(calcDto, "模拟订单货物类型:" + calcDto.getCargoType());
        putMsg(calcDto, "模拟订单重量:" + calcDto.getCusWeight().doubleValue() + calcDto.getWeightUnit());
        putMsg(calcDto, "模拟订单起始地:" + calcDto.getStartAddressStr());
        putMsg(calcDto, "模拟订单起始港:" + calcDto.getStartPort());
        putMsg(calcDto, "模拟订单目的地:" + calcDto.getEndAddressStr());
        putMsg(calcDto, "模拟订单目的港:" + calcDto.getEndPort());
        putMsg(calcDto, "模拟订单收货地:" + calcDto.getReceiveAddressStr());
        putMsg(calcDto, "模拟订单声明价值:" + calcDto.getAnnouncePrice().doubleValue());


        //获取产品
        Product prod = productService.findShortActivedByUid(calcObject.getProductUid());
        putMsg(calcDto, "获取到下单产品:" + prod.getName() + " id:[" + prod.getId() + "]");

        //已按节点序号排序
        List<ProductServiceRel> prodServices = productServiceRelService.findByProdId(prod.getId());
        Map<String, List<ProductServiceRel>> prodServicesMap = new HashMap<>(16);
        //当前产品所有的服务类型编码(serviceTypeCode)
        List<String> serviceTypes = new ArrayList<>();
        List<String> readables = new ArrayList<>();
        if (!CollectionUtils.isEmpty(prodServices)) {
            prodServices.forEach(item -> {
                if (!serviceTypes.contains(item.getServiceTypeCode())) {
                    serviceTypes.add(item.getServiceTypeCode());
                    readables.add(item.getServiceTypeCode() + "[" + item.getServiceProperty().getCode() + "]");
                }
                prodServicesMap.putIfAbsent(item.getServiceTypeCode(), new ArrayList<>());
                prodServicesMap.get(item.getServiceTypeCode()).add(item);
                //将产品的所有的可选服务全部添加进去
                if (calcObject.isCalcAlloption()) {
                    if (item.getServiceProperty() == ProductServicePropertyEnum.OPTIONAL &&
                            !calcDto.getOptionServiceTypes().contains(item.getServiceTypeCode())) {
                        calcDto.getOptionServiceTypes().add(item.getServiceTypeCode());
                    }
                }
            });
            if (calcObject.isCalcAlloption()) {
                putMsg(calcDto, "模拟订单使用产品的全部可选服务类型:" + StringUtils.toCommaString(calcDto.getOptionServiceTypes()));
            }
        }
        if (!calcObject.isCalcAlloption()) {
            calcDto.setOptionServiceTypes(calcObject.getOptionServices());
            putMsg(calcDto, "模拟订单使用已选的可选服务类型:" + StringUtils.toCommaString(calcDto.getOptionServiceTypes()));
        }
        if (!CollectionUtils.isEmpty(serviceTypes)) {
            assign.setStTypes(StringUtils.toCommaString(readables) + "|" +
                    StringUtils.toCommaString(calcDto.getOptionServiceTypes()));

            putMsg(calcDto, "产品所有的服务类型:" + StringUtils.toCommaString(readables));
            putMsg(calcDto, "将按照产品服务节点的顺序计算\r\n");
            //遍历按顺序生成的服务类型
            serviceTypes.forEach(serviceType -> {
                boolean isOption = false;
                boolean isMatchOk = false;
                try {
                    putMsg(calcDto, "\r\n--------------------" + serviceType + "-----------------");
                    List<ProductServiceRel> typedServices = prodServicesMap.get(serviceType);
                    isOption = isOptionService(typedServices.get(0).getServiceProperty());
                    isMatchOk = compareAndGetMatchService(serviceType, typedServices, calcDto, assign, null);
                } catch (BizException e) {
                    putMsg(calcDto, "遍历按顺序生成的服务类型,计算失败，原因:" + e.getMsg());
                } catch (Exception e) {
                    putMsg(calcDto, StringUtils.arrayToDelimitedString(e.getStackTrace(), "\r\n"));
                    putMsg(calcDto, "遍历按顺序生成的服务类型,计算失败，原因:" + e.getClass() + e.getMessage());
                }
                //判断是否分派成功
                checkIsAssignOk(calcObject, assign, isOption, isMatchOk);

            });
        } else {
            assign.setIsAssignOk(Boolean.FALSE);
            putMsg(calcDto, "产品竟然没有筛选到服务,计算失败");
        }
        assign.setCalcMsg(calcDto.getCalcMsg().toString());
        if (StringUtils.isEmpty(assign.getSt001Uid()) &&
                StringUtils.isEmpty(assign.getSt002Uid()) &&
                StringUtils.isEmpty(assign.getSt003Uid()) &&
                StringUtils.isEmpty(assign.getSt004Uid()) &&
                StringUtils.isEmpty(assign.getSt005Uid()) &&
                StringUtils.isEmpty(assign.getSt006Uid())
                ) {
            assign.setIsAssignOk(Boolean.FALSE);
        }
        return assign;
    }

    /**
     * 是否分派成功
     */
    private void checkIsAssignOk(QuotationCalcObject calcObject, OrdServiceAssign assign, boolean isOption, boolean isMatchOk) {
        //分派成功
        if (isMatchOk) {
            assign.setIsAssignOk(Boolean.TRUE);
            return;
        }

        //分派失败
        //必||主
        if (!isOption) {
            assign.setIsAssignOk(Boolean.FALSE);
        }
        //可选
        else {
            //试算都认为成功
            if (calcObject.isTry()) {
                assign.setIsAssignOk(Boolean.TRUE);
            } else {
                assign.setIsAssignOk(Boolean.FALSE);
            }
        }
    }

    /**
     * @param serviceType   服务类型编码
     * @param typedServices 当前产品配制的所有服务类型编码
     * @param calcDto       计算对象
     * @param assign        分配对象
     * @param osaHistory    历史分派的服务
     */
    private boolean compareAndGetMatchService(String serviceType,
                                              List<ProductServiceRel> typedServices,
                                              OrdServiceAssignCalcDto calcDto, OrdServiceAssign assign,
                                              OrdServiceAssign osaHistory) {
        boolean isOption = isOptionService(typedServices.get(0).getServiceProperty());
        if (isOption && !calcDto.getOptionServiceTypes().contains(serviceType)) {
            putMsg(calcDto, "可选服务类型" + serviceType + "没有选择,此服务类型的计算忽略");
            return true;
        }
        putMsg(calcDto, "服务类型:" + serviceType + "下的服务列表");
        for (ProductServiceRel item : typedServices) {
            putMsg(calcDto, "服务:" + item.getServiceUid());
        }

        String serviceUid;

        List<ProductServiceRel> baseLeftServices;
        switch (serviceType) {
            //揽收 //仓储  //清关 //配送 //其他
            case TopServiceTypes.RECEIVE:
            case TopServiceTypes.STORE:
            case TopServiceTypes.PORT:
            case TopServiceTypes.DELIVERY:
            case TopServiceTypes.OTHER:
                //  过滤基本条件
                baseLeftServices = filterNormalConditions(typedServices, calcDto, serviceType);
                break;

            //干线
            case TopServiceTypes.MAINLINE:
                //  过滤干线基本条件
                baseLeftServices = filterMainlineBaseConditions(typedServices, calcDto);
                break;

            default:
                throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL, "serviceType:" + serviceType);

        }
        if (CollectionUtils.isEmpty(baseLeftServices)) {
            assign.setIsAssignOk(Boolean.FALSE);
            putMsg(calcDto, serviceType + "基本条件选择之后，没有匹配到合适的服务，计算失败");
            return false;
        }

        // 过滤固定条件
        List<ProductServiceRel> fixedLeftServices = filterFixedConditions(baseLeftServices, calcDto);

        if (CollectionUtils.isEmpty(fixedLeftServices)) {
            assign.setIsAssignOk(Boolean.FALSE);
            putMsg(calcDto, serviceType + "固定条件选择之后，没有匹配到合适的服务，计算失败");
            return false;
        }

        //过滤择优条件肯定有一个匹配到
        ProductServiceRel wellLeftService = filterWellConditions(fixedLeftServices, calcDto);
        serviceUid = wellLeftService.getServiceUid();

        Service service = serviceService.findActivedByUid(serviceUid);

        putMsg(calcDto, "择优之后，匹配到服务的id:" + service.getId() + " uid:" + service.getUid() + " name:" + service.getName());


        // 获取运单服务商的渠道号码
        switch (serviceType) {
            case TopServiceTypes.RECEIVE:
                assign.setSt001Id(service.getId());
                assign.setSt001Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt001Uid()) && !StringUtils.isEmpty(osaHistory.getSt001ChannelNo())) {
                        assign.setSt001ChannelNo(osaHistory.getSt001ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt001ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }
                break;
            case TopServiceTypes.STORE:
                assign.setSt002Id(service.getId());
                assign.setSt002Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt002Uid()) && !StringUtils.isEmpty(osaHistory.getSt002ChannelNo())) {
                        assign.setSt002ChannelNo(osaHistory.getSt002ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt002ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }
                break;
            case TopServiceTypes.PORT:
                assign.setSt003Id(service.getId());
                assign.setSt003Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt003Uid()) && !StringUtils.isEmpty(osaHistory.getSt003ChannelNo())) {
                        assign.setSt003ChannelNo(osaHistory.getSt003ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt003ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }

                break;
            case TopServiceTypes.MAINLINE:
                assign.setSt004Id(service.getId());
                assign.setSt004Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt004Uid()) && !StringUtils.isEmpty(osaHistory.getSt004ChannelNo())) {
                        assign.setSt004ChannelNo(osaHistory.getSt004ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt004ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }
                if (calcDto.getClassMap().get(serviceUid) != null) {
                    assign.setSt004ClassId(calcDto.getClassMap().get(serviceUid));
                    putMsg(calcDto, "\r\n匹配到服务的id:" + service.getId() + "的仓位:" + assign.getSt004ClassId());
                }
                break;
            case TopServiceTypes.DELIVERY:
                assign.setSt005Id(service.getId());
                assign.setSt005Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt005Uid()) && !StringUtils.isEmpty(osaHistory.getSt005ChannelNo())) {
                        assign.setSt005ChannelNo(osaHistory.getSt005ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt005ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }
                break;
            case TopServiceTypes.OTHER:
                assign.setSt006Id(service.getId());
                assign.setSt006Uid(serviceUid);
                if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                    if (osaHistory != null && serviceUid.equals(osaHistory.getSt006Uid()) && !StringUtils.isEmpty(osaHistory.getSt006ChannelNo())) {
                        assign.setSt006ChannelNo(osaHistory.getSt006ChannelNo());
                    } else {
                        String channelNo = fetchChannelNo(serviceType, assign.getOrderNo(), serviceUid);
                        assign.setSt006ChannelNo(channelNo);
                        putMsg(calcDto, "获取服务的渠道单号:" + channelNo);
                    }
                }
                break;
            default:
                throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_ILLEGAL, "serviceType:" + serviceType);
        }
        return true;
    }

    private String fetchChannelNo(String serviceType, String orderNo, String serviceUid) {
        if (StringUtils.isEmpty(orderNo)) {
            return "";
        }

        SerialNoManagement channelNoObj = noManagementService.useChannelNo(serviceUid, orderNo, 0);
        if (channelNoObj == null) {
            return "";
        }
        //查重
        List<OrdServiceAssign> assigns = findByServiceUidAndChannelNo(serviceType, serviceUid, channelNoObj.getSerialNumber());
        int reTry = 0;
        //检查10次
        while (!CollectionUtils.isEmpty(assigns) && reTry < RETRYCHANNELCOUNT) {
            channelNoObj = noManagementService.useChannelNo(serviceUid, orderNo,channelNoObj.getId());
            assigns = findByServiceUidAndChannelNo(serviceType, serviceUid, channelNoObj.getSerialNumber());
            if (!CollectionUtils.isEmpty(assigns)) {
                channelNoObj = null;
            }
            reTry++;
        }

        return channelNoObj==null?"":channelNoObj.getSerialNumber();
    }

    private List<OrdServiceAssign> findByServiceUidAndChannelNo(String serviceType, String serviceUid, String channelNo) {
        Map<String, Object> params = new HashMap<>(16);
        params.put("st", serviceType);
        params.put("serviceUid", serviceUid);
        params.put("channelNo", channelNo);
        return dao.findForList("OrdServiceAssignMapper.findByServiceUidAndChannelNo", params, OrdServiceAssign.class);
    }


    /**
     * 过滤干线的基本条件
     */
    private List<ProductServiceRel> filterMainlineBaseConditions(List<ProductServiceRel> typedServices,
                                                                 OrdServiceAssignCalcDto calcDto) {
        putMsg(calcDto, "开始计算服务类型干线的基本条件");
        List<ProductServiceRel> baseLeftServices = new ArrayList<>();
        typedServices.forEach(mainlineService -> {
            String serviceUid = mainlineService.getServiceUid();
            Service service = serviceService.findActivedByUid(serviceUid);

            if (service == null || !service.getIsOnline()) {
                putMsg(calcDto, "过滤服务" + serviceUid + ",原因：此服务已停用");
                return;
            }
            List<ServiceTypeRel> stRels = serviceTypeRelService.findByRefId(service.getId());
            ServiceTypeRel stRel = stRels.get(0);
            //兼容旧数据
            if (stRel.getSubServiceTypeCode() == null) {
                stRel.setSubServiceTypeCode("-");
            }
            putMsg(calcDto, "");
            //1.服务范围
            //海运 空运itemDetail为机场 港口id
            switch (stRel.getSubServiceTypeCode()) {
                case SubServiceTypes.MAINLINE_AIR: {
                    //空的订单号，说明是试算的服务分派
                    if (StringUtils.isEmpty(calcDto.getOrderNo())) {
                        List<Airport> sports = airportService.portFliter(calcDto.getFromCountry(), calcDto.getFromProvince(), calcDto.getFromCity());
                        calcDto.setStartPorts(EntityUtils.getIntPropListFromBeans(sports, "id"));
                        List<Airport> eports = airportService.portFliter(calcDto.getToCountry(), calcDto.getToProvince(), calcDto.getToCity());
                        calcDto.setEndPorts(EntityUtils.getIntPropListFromBeans(eports, "id"));
                    }
                    if (stRel.getLineId() == null || stRel.getLineId() == 0) {
                        putMsg(calcDto, "匹配服务" + serviceUid + ",原因：此服务的航线为空，默认匹配所有的");
                        break;
                    }
                    //起止港是否包含范围
                    List<AirlineDetail> ports = airlineDetailService.findByAirlineId(stRel.getLineId());
                    List<Integer> portIds = EntityUtils.getIntPropListFromBeans(ports, "airportId");
                    if (CollectionUtils.isEmpty(portIds)) {
                        putMsg(calcDto, "过滤服务" + serviceUid + ",原因：此航线的机场为空？？？？");
                        return;
                    }
                    putMsg(calcDto, "服务" + serviceUid + ",为干线空运,航线id[" + stRel.getLineId() + "],机场:" +
                            StringUtils.collectionToCommaDelimitedString(portIds));

                    if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                        //服务的航线第一个港口不匹配
                        if (portIds.get(0) != calcDto.getStartPort()) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线起飞机场不匹配订单的起运机场");
                            return;
                        }
                        portIds.remove(0);
                        //服务的航线其他的港口不匹配
                        if (!portIds.contains(calcDto.getEndPort())) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线起飞目的机场不匹配订单的目的机场");
                            return;
                        }
                    } else {
                        boolean hasSPort = false;
                        for (Integer portId : portIds) {
                            if (calcDto.getStartPorts().contains(portId)) {
                                hasSPort = true;
                            }
                        }
                        boolean hasEPort = false;
                        for (Integer portId : portIds) {
                            if (calcDto.getEndPorts().contains(portId)) {
                                hasEPort = true;
                            }
                        }

                        if (!hasSPort) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线起飞机场不匹配订单的起运机场");
                            return;
                        }
                        if (!hasEPort) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线起飞目的机场不匹配订单的目的机场");
                            return;
                        }
                    }

                    putMsg(calcDto, "匹配服务" + serviceUid + ",航线的机场匹配订单的起运港和目的港");
                    //计算仓位信息
                    if (!hasAirClass(serviceUid, mainlineService.getItemDetail(), calcDto)) {
                        return;
                    }

                    break;
                }
                case SubServiceTypes.MAINLINE_SHIP: {
                    //空的订单号，说明是试算的服务分派
                    if (StringUtils.isEmpty(calcDto.getOrderNo())) {
                        List<Port> sports = portService.portFliter(calcDto.getFromCountry(), calcDto.getFromProvince(), calcDto.getFromCity());
                        calcDto.setStartPorts(EntityUtils.getIntPropListFromBeans(sports, "id"));
                        List<Port> eports = portService.portFliter(calcDto.getToCountry(), calcDto.getToProvince(), calcDto.getToCity());
                        calcDto.setEndPorts(EntityUtils.getIntPropListFromBeans(eports, "id"));
                    }
                    if (stRel.getLineId() == null || stRel.getLineId() == 0) {
                        putMsg(calcDto, "匹配服务" + serviceUid + ",原因：此服务的航线为空，默认匹配所有的");
                        break;
                    }
                    //起止港是否包含范围
                    List<ShippingLineDetail> ports = shippingLineDetailService.findByShippingLineId(stRel.getLineId());
                    List<Integer> portIds = EntityUtils.getIntPropListFromBeans(ports, "portId");
                    if (CollectionUtils.isEmpty(portIds)) {
                        putMsg(calcDto, "过滤服务" + serviceUid + ",原因：此航线的港口为空？？？？");
                        return;
                    }
                    putMsg(calcDto, "服务" + serviceUid + ",为干线海运,航线id[" + stRel.getLineId() + "],港口:" +
                            StringUtils.collectionToCommaDelimitedString(portIds));

                    //服务的航线第一个港口不匹配
                    if (!StringUtils.isEmpty(calcDto.getOrderNo())) {
                        if (portIds.get(0) != calcDto.getStartPort()) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线出发港不匹配订单的起运港");
                            return;
                        }
                        portIds.remove(0);
                        //服务的航线其他的港口不匹配
                        if (!portIds.contains(calcDto.getEndPort())) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线目的港不匹配订单的目的港");
                            return;
                        }

                    } else {
                        boolean hasSPort = false;
                        for (Integer portId : portIds) {
                            if (calcDto.getStartPorts().contains(portId)) {
                                hasSPort = true;
                            }
                        }
                        boolean hasEPort = false;
                        for (Integer portId : portIds) {
                            if (calcDto.getEndPorts().contains(portId)) {
                                hasEPort = true;
                            }
                        }

                        if (!hasSPort) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线出发港不匹配订单的起运港");
                            return;
                        }
                        if (!hasEPort) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：航线目的港不匹配订单的目的港");
                            return;
                        }
                    }


                    //计算仓位信息
                    if (!hasShipClass(serviceUid, mainlineService.getItemDetail(), calcDto)) {
                        return;
                    }
                    break;
                }
                default:
                    //1.如果产品服务的范围为空，则默认包含所有地址
                    putMsg(calcDto, "服务" + serviceUid + ",为干线-其他");
                    if (!StringUtils.isEmpty(mainlineService.getItemDetail())) {
                        String[] regions = mainlineService.getItemDetail().split(ProductServiceRegionConstants.REGIONSPLIT);
                        if (!matchAddress(regions[0], calcDto.getStartAddressStr()) ||
                                !matchAddress(regions.length > 1 ? regions[1] : regions[0], calcDto.getEndAddressStr())) {
                            putMsg(calcDto, "过滤服务" + serviceUid + ",原因：起运地址或者目的地址不匹配");
                            return;
                        }
                    }
                    break;
            }
            putMsg(calcDto, "服务" + serviceUid + ",产品内设置的服务范围匹配");
            //2.货物类型
            List<ProductServiceRelCargotypeInfo> pscinfos = productServiceCargotypeRelService.findByPsId(mainlineService.getId());
            if (!CollectionUtils.isEmpty(pscinfos)) {
                List<String> cargos = EntityUtils.getStringPropListFromBeans(pscinfos, "cargoTypeCode");
                if (!cargos.contains(calcDto.getCargoType())) {
                    putMsg(calcDto, "过滤服务:" + mainlineService.getServiceUid() +
                            "，产品中此服务设置的货物类型[" + StringUtils.toCommaString(cargos) + "]不匹配");
                    return;
                } else {
                    putMsg(calcDto, "匹配服务:" + mainlineService.getServiceUid() +
                            "，产品中此服务设置的货物类型[" + StringUtils.toCommaString(cargos) + "]匹配");
                }

            }
            //3.重量限制
            setStandardWeight(calcDto);

            if (mainlineService.getWeightLimitMin() != null && mainlineService.getWeightLimitMin().compareTo(calcDto.getCusWeight()) > 0) {
                putMsg(calcDto, "过滤服务:" + mainlineService.getServiceUid() +
                        "，产品中设置最小限重[" + mainlineService.getWeightLimitMin().doubleValue() + "]不匹配");
                return;
            }
            if (mainlineService.getWeightLimitMax() != null && mainlineService.getWeightLimitMax().compareTo(calcDto.getCusWeight()) < 0) {
                putMsg(calcDto, "过滤服务:" + mainlineService.getServiceUid() +
                        "，产品中设置最小限重[" + mainlineService.getWeightLimitMin().doubleValue() + "]不匹配");
                return;
            }
            String startVal = mainlineService.getWeightLimitMin() == null ? "" : mainlineService.getWeightLimitMin().toPlainString();
            String endVal = mainlineService.getWeightLimitMax() == null ? "" : mainlineService.getWeightLimitMax().toPlainString();
            putMsg(calcDto, "匹配服务:" + mainlineService.getServiceUid() +
                    "，产品中此服务设置的重量限制[" + startVal + "," + endVal + "]匹配");
            baseLeftServices.add(mainlineService);
        });

        return baseLeftServices;
    }

    private void setStandardWeight(OrdServiceAssignCalcDto calcDto) {
        String kg = "kg";
        if (!kg.equals(calcDto.getWeightUnit())) {
            BigDecimal standardWeight = unitConversionService.getTargetWeight(calcDto.getCusWeight(), calcDto.getWeightUnit(), kg);
            calcDto.setCusWeight(standardWeight);
            calcDto.setWeightUnit(kg);
            putMsg(calcDto, "换算订单重量:" + standardWeight.doubleValue() + kg);
        }
    }

    /**
     * 过滤固定条件
     */
    private List<ProductServiceRel> filterFixedConditions(List<ProductServiceRel> baseLeftServices, OrdServiceAssignCalcDto calcDto) {
        List<ProductServiceRel> fixedLeftServices = new ArrayList<>();

        if (CollectionUtils.isEmpty(baseLeftServices)) {
            return fixedLeftServices;
        }
        baseLeftServices.forEach(baseLeftService -> {
            //客户等级
            if (baseLeftService.getCustomerLevel() != null && baseLeftService.getCustomerLevel() != 0) {
                if (ABOVE.equals(baseLeftService.getCustomerLevelExt())) {
                    if (calcDto.getCusLevel() < baseLeftService.getCustomerLevel()) {
                        putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                                "-" + baseLeftService.getServiceUid() + "的客户等级不匹配[订单客户等级" +
                                calcDto.getCusLevel() + "<最低" + baseLeftService.getCustomerLevel() + "]");
                        return;
                    }
                } else if (BELOW.equals(baseLeftService.getCustomerLevelExt())) {
                    if (calcDto.getCusLevel() > baseLeftService.getCustomerLevel()) {
                        putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                                "-" + baseLeftService.getServiceUid() + "的客户等级不匹配[订单客户等级" +
                                calcDto.getCusLevel() + ">最高" + baseLeftService.getCustomerLevel() + "]");
                        return;
                    }
                } else {
                    //只能相等
                    if (calcDto.getCusLevel() != baseLeftService.getCustomerLevel()) {
                        putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                                "-" + baseLeftService.getServiceUid() + "的客户等级不匹配[订单客户等级" +
                                calcDto.getCusLevel() + "!=" + baseLeftService.getCustomerLevel() + "]");
                        return;
                    }
                }
            }
            putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                    "-" + baseLeftService.getServiceUid() + "的客户等级匹配]");
            //申报价值
            if (baseLeftService.getDeclaredValueMin() != null) {
                if (calcDto.getAnnouncePrice().compareTo(baseLeftService.getDeclaredValueMin()) < 0) {
                    putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                            "-" + baseLeftService.getServiceUid() + "的申报价值下限不匹配[订单申报值" +
                            calcDto.getAnnouncePrice().doubleValue() + "<下限" + baseLeftService.getDeclaredValueMin().doubleValue() + "]");
                    return;
                }
            }
            if (baseLeftService.getDeclaredValueMax() != null) {
                if (calcDto.getAnnouncePrice().compareTo(baseLeftService.getDeclaredValueMax()) > 0) {
                    putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                            "-" + baseLeftService.getServiceUid() + "的申报价值上限不匹配[订单申报值" +
                            calcDto.getAnnouncePrice().doubleValue() + ">上限" + baseLeftService.getDeclaredValueMax().doubleValue() + "]");
                    return;
                }
            }
            putMsg(calcDto, "产品" + baseLeftService.getProductId() + "中服务" + baseLeftService.getServiceTypeCode() +
                    "-" + baseLeftService.getServiceUid() + "的申报价值匹配]");
            fixedLeftServices.add(baseLeftService);
        });


        return fixedLeftServices;
    }

    /**
     * 过滤择优条件
     */

    private ProductServiceRel filterWellConditions(List<ProductServiceRel> fixedLeftServices, OrdServiceAssignCalcDto calcDto) {
        if (fixedLeftServices.size() == 1) {
            putMsg(calcDto, "固定条件筛选完之后，匹配到唯一的服务，不必择优：" + fixedLeftServices.get(0).getServiceUid());
            return fixedLeftServices.get(0);
        }
        putMsg(calcDto, "固定条件筛选完之后，匹配多个服务：" +
                StringUtils.toCommaString(EntityUtils.getStringPropListFromBeans(fixedLeftServices, "serviceUid")));

        putMsg(calcDto, "\r\n开始匹配择优条件");
        List<WellCompareObj> compares = new ArrayList<>();
        fixedLeftServices.forEach(fixedLeftService -> {
            putMsg(calcDto, "\r\n开始计算服务:" + fixedLeftService.getServiceUid() + "的成本价");
            QuotationCalcObject quotationCalcObject = null;
            try {
                quotationCalcObject = quotationCalcService.calcServiceCostPrice(fixedLeftService.getServiceUid(),
                        calcDto.getOrderDate(), 0, calcDto.getCargoType(),
                        calcDto.getCusWeight(), BigDecimal.ZERO, calcDto.getWeightUnit(),
                        calcDto.getFromCountry(),
                        calcDto.getFromProvince(),
                        calcDto.getFromCity(),
                        calcDto.getFromArea(),
                        calcDto.getFromStreet(),
                        calcDto.getFromPostcode(),
                        calcDto.getToCountry(),
                        calcDto.getToProvince(),
                        calcDto.getToCity(),
                        calcDto.getToArea(),
                        calcDto.getToStreet(),
                        calcDto.getToPostcode()
                );
                putMsg(calcDto, "计算过程:" + StringUtils.toCommaString(quotationCalcObject.getCalcMsgs()));
            } catch (BizException e) {
                putMsg(calcDto, "服务:" + fixedLeftService.getServiceUid() + "成本价计算异常:" + e.getMsg());
            } catch (Exception e) {
                putMsg(calcDto, "服务:" + fixedLeftService.getServiceUid() + "成本价计算异常:" + e.getClass() + e.getMessage());
                putMsg(calcDto, StringUtils.arrayToDelimitedString(e.getStackTrace(), "\r\n"));
            }
            WellCompareObj obj = new WellCompareObj(fixedLeftService);
            //价格
            if (quotationCalcObject != null && !CollectionUtils.isEmpty(quotationCalcObject.getResult())) {
                QuotationCalcResult result = quotationCalcObject.getResult().get(0);
                if (result.isHasResult()) {
                    BigDecimal amount = result.getAmount();
                    putMsg(calcDto, "计算成功，金额:" + amount.doubleValue());
                    obj.setPrice(amount);
                } else {
                    putMsg(calcDto, "计算失败，计算金额以极大的数计算:" + Integer.MAX_VALUE);
                    obj.setPrice(new BigDecimal(Integer.MAX_VALUE));
                }
            } else {
                putMsg(calcDto, "计算失败，计算金额以极大的数计算:" + Integer.MAX_VALUE);
                obj.setPrice(new BigDecimal(Integer.MAX_VALUE));
            }
            //价格权重
            if (fixedLeftService.getPriceWeighting() != null) {
                BigDecimal fixedPriceWeight = new BigDecimal(fixedLeftService.getPriceWeighting());
                putMsg(calcDto, "设置的价格权重：" + fixedLeftService.getPriceWeighting().doubleValue());
                obj.setPriceCalcWeight(fixedPriceWeight.multiply(obj.getPrice()));
                putMsg(calcDto, "计算后的价格权重(计算金额*价格权重)：" + obj.getPriceCalcWeight().doubleValue());
            } else {
                obj.setPriceCalcWeight(new BigDecimal(Integer.MAX_VALUE));
                putMsg(calcDto, "设置的价格权重为空，以极大的数计算价格权重：" + Integer.MAX_VALUE);
            }


            ServiceEffectiveness serviceEffectiveness = serviceEffectivenessService.
                    findByServiceUid(fixedLeftService.getServiceUid());

            //时效
            if (serviceEffectiveness != null) {
                obj.setTime(new BigDecimal(serviceEffectiveness.getTimeCost()));
                putMsg(calcDto, "获取到服务：" + fixedLeftService.getServiceUid() + "的时效为:" + serviceEffectiveness.getTimeCost());
            } else {
                obj.setTime(new BigDecimal(Integer.MAX_VALUE));
                putMsg(calcDto, "获取到服务：" + fixedLeftService.getServiceUid() + "时效为空，以极大的数计算时效权重：" + Integer.MAX_VALUE);
            }
            //时效权重
            if (fixedLeftService.getAgingWeighting() != null) {
                putMsg(calcDto, "设置的时效权重：" + fixedLeftService.getAgingWeighting().doubleValue());
                BigDecimal fixedAgingWeight = new BigDecimal(fixedLeftService.getAgingWeighting());
                obj.setTimeCalcWeight(fixedAgingWeight.multiply(obj.getTime()));
                putMsg(calcDto, "计算后的时效权重(时效*时效权重)：" + obj.getTimeCalcWeight().doubleValue());
            } else {
                obj.setTimeCalcWeight(BigDecimal.ZERO);
            }
            compares.add(obj);

        });

        //Fore xample
        //A服务：1.2元 15小时
        //B服务：1.5元 25小时
        //C服务：2.5元 20小时

        //价格排序 按价格升序排 价格低的在前面 like: 1.2元(1)  1.5元(2) 2.0元(3)
        compares.sort((item1, item2) -> item1.getPriceCalcWeight().compareTo(item2.getPriceCalcWeight()));
        for (int index = 0; index < compares.size(); index++) {
            compares.get(index).setPriceOrder(index);
        }

        putMsg(calcDto, "价格排序后，服务的顺序：" + getServiceUids(compares));

        //时效排序 按时效升序排 时效快的在前面 like: 15小时(1)  20小时(2) 25小时(3)
        compares.sort((item1, item2) -> item1.getTimeCalcWeight().compareTo(item2.getTimeCalcWeight()));
        for (int index = 0; index < compares.size(); index++) {
            compares.get(index).setTimeOrder(index);
        }
        putMsg(calcDto, "时效排序后，服务的顺序：" + getServiceUids(compares));

        //上面两个排序的和再排序        出现
        compares.forEach(item -> item.setCalcOrder(item.getPriceOrder() + item.getTimeOrder()));
        //A服务：1.2元(1) 15小时(1)   合计：2
        //B服务：1.5元(2) 25(3)      合计：5
        //C服务：2.5元(3) 20小时(2)   合计：5
        compares.sort((item1, item2) -> item1.getCalcOrder() - item2.getCalcOrder());

        putMsg(calcDto, "综合排序后，服务的顺序：" + getServiceUids(compares));

        //只取排序第一的即为价格低时效快的服务
        putMsg(calcDto, "择优匹配到的服务：" + compares.get(0).getService().getServiceUid());
        return compares.get(0).getService();
    }

    /**
     * 过滤服务基本条件
     */
    private List<ProductServiceRel> filterNormalConditions(List<ProductServiceRel> typedServices, OrdServiceAssignCalcDto calcDto, String serviceType) {
        putMsg(calcDto, "开始计算服务类型" + serviceType + "的基本条件");
        List<ProductServiceRel> baseLeftServices = new ArrayList<>();
        typedServices.forEach(baseService -> {

            String serviceUid = baseService.getServiceUid();
            Service service = serviceService.findActivedByUid(serviceUid);

            if (service == null || !service.getIsOnline()) {
                putMsg(calcDto, "过滤服务" + serviceUid + ",原因：此服务已停用");
                return;
            }

            //1.服务范围 如果产品服务的范围为空，则默认包含所有地址
            if (!StringUtils.isEmpty(baseService.getItemDetail())) {
                //匹配地址
                String address = "";

                //揽收 取揽收的地址
                if (TopServiceTypes.RECEIVE.equals(serviceType)) {
                    address = calcDto.getReceiveAddressStr();
                }

                //仓储 清关 其他 取发件地址
                if (TopServiceTypes.STORE.equals(serviceType) || TopServiceTypes.PORT.equals(serviceType) ||
                        TopServiceTypes.OTHER.equals(serviceType)) {
                    address = calcDto.getStartAddressStr();
                }

                //配送 取收件人地址
                if (TopServiceTypes.DELIVERY.equals(serviceType)) {
                    address = calcDto.getEndAddressStr();
                }
                if (!matchAddress(baseService.getItemDetail(), address)) {
                    putMsg(calcDto, "过滤服务:" + baseService.getServiceUid() + "订单地址:" + address +
                            "，与产品中此服务设置的范围" + baseService.getItemDetail() + "不匹配");
                    return;
                }
                putMsg(calcDto, "匹配服务:" + baseService.getServiceUid() + "，订单地址:" + address +
                        "，与产品中此服务设置的范围匹配" + baseService.getItemDetail() + "匹配");
            } else {
                putMsg(calcDto, "匹配服务:" + baseService.getServiceUid() + "，产品中此服务设置的范围为空，认为匹配任何地址");
            }

            //2.货物类型
            List<ProductServiceRelCargotypeInfo> pscinfos = productServiceCargotypeRelService.findByPsId(baseService.getId());
            if (!CollectionUtils.isEmpty(pscinfos)) {
                List<String> cargos = EntityUtils.getStringPropListFromBeans(pscinfos, "cargoTypeCode");
                if (!cargos.contains(calcDto.getCargoType())) {
                    putMsg(calcDto, "过滤服务:" + baseService.getServiceUid() +
                            "，产品中此服务设置的货物类型[" + StringUtils.toCommaString(cargos) + "]不匹配");
                    return;
                }
                putMsg(calcDto, "匹配服务:" + baseService.getServiceUid() +
                        "，产品中此服务设置的货物类型[" + StringUtils.toCommaString(cargos) + "]匹配");
            }


            //3.重量限制
            setStandardWeight(calcDto);

            if (baseService.getWeightLimitMin() != null && calcDto.getCusWeight().compareTo(baseService.getWeightLimitMin()) < 0) {
                putMsg(calcDto, "过滤服务:" + baseService.getServiceUid() +
                        "，产品中设置最小限重[" + baseService.getWeightLimitMin().doubleValue() + "]不匹配");
                return;
            }
            if (baseService.getWeightLimitMax() != null && calcDto.getCusWeight().compareTo(baseService.getWeightLimitMax()) > 0) {
                putMsg(calcDto, "过滤服务:" + baseService.getServiceUid() +
                        "，产品中设置最大限重[" + baseService.getWeightLimitMax().doubleValue() + "]不匹配");
                return;
            }
            String startVal = baseService.getWeightLimitMin() == null ? "" : baseService.getWeightLimitMin().toPlainString();
            String endVal = baseService.getWeightLimitMax() == null ? "" : baseService.getWeightLimitMax().toPlainString();
            putMsg(calcDto, "匹配服务:" + baseService.getServiceUid() +
                    "，产品中此服务设置的重量限制[" + startVal + "," + endVal + "]匹配");
            baseLeftServices.add(baseService);
        });

        return baseLeftServices;

    }


    @Override
    public ProductServiceRel filterWellConditions(List<ProductServiceRel> fixedLeftServices, QuotationCalcObject calcObject) {
        if (fixedLeftServices.size() == 1) {
            return fixedLeftServices.get(0);
        }
        List<WellCompareObj> compares = new ArrayList<>();
        fixedLeftServices.forEach(fixedLeftService -> {
            QuotationCalcObject quotationCalcObject = null;
            try {
                quotationCalcObject = quotationCalcService.calcServiceCostPrice(fixedLeftService.getServiceUid(),
                        calcObject.getOrderDate(), 0, calcObject.getCargoTypeCode(),
                        calcObject.getCalcWeight(), BigDecimal.ZERO, calcObject.getWeightUnit(),
                        calcObject.getFromCountry(),
                        calcObject.getFromProvince(),
                        calcObject.getFromCity(),
                        calcObject.getFromArea(),
                        calcObject.getFromStreet(),
                        calcObject.getFromPostcode(),
                        calcObject.getToCountry(),
                        calcObject.getToProvince(),
                        calcObject.getToCity(),
                        calcObject.getToArea(),
                        calcObject.getToStreet(),
                        calcObject.getToPostcode()
                );
                calcObject.putMsg("计算过程:" + StringUtils.toCommaString(quotationCalcObject.getCalcMsgs()));
            } catch (BizException e) {
                calcObject.putMsg("服务:" + fixedLeftService.getServiceUid() + "成本价计算异常:" + e.getMsg());
            } catch (Exception e) {
                calcObject.putMsg("服务:" + fixedLeftService.getServiceUid() + "成本价计算异常:" + e.getClass() + e.getMessage());
            }
            WellCompareObj obj = new WellCompareObj(fixedLeftService);
            //价格
            if (quotationCalcObject != null && !CollectionUtils.isEmpty(quotationCalcObject.getResult())) {
                BigDecimal amount = quotationCalcObject.getResult().get(0).getAmount();
                calcObject.putMsg("计算成功，金额:" + amount.doubleValue());
                obj.setPrice(amount);
            } else {
                calcObject.putMsg("计算失败，计算金额以极大的数计算:" + Integer.MAX_VALUE);
                obj.setPrice(new BigDecimal(Integer.MAX_VALUE));
            }
            //价格权重
            if (fixedLeftService.getPriceWeighting() != null) {
                BigDecimal fixedPriceWeight = new BigDecimal(fixedLeftService.getPriceWeighting());
                calcObject.putMsg("设置的价格权重：" + fixedLeftService.getPriceWeighting().doubleValue());
                obj.setPriceCalcWeight(fixedPriceWeight.multiply(obj.getPrice()));
                calcObject.putMsg("计算后的价格权重(计算金额*价格权重)：" + obj.getPriceCalcWeight().doubleValue());
            } else {
                obj.setPriceCalcWeight(new BigDecimal(Integer.MAX_VALUE));
                calcObject.putMsg("设置的价格权重为空，以极大的数计算价格权重：" + Integer.MAX_VALUE);
            }


            ServiceEffectiveness serviceEffectiveness = serviceEffectivenessService.
                    findByServiceUid(fixedLeftService.getServiceUid());

            //时效
            if (serviceEffectiveness != null) {
                obj.setTime(new BigDecimal(serviceEffectiveness.getTimeCost()));
                calcObject.putMsg("获取到服务：" + fixedLeftService.getServiceUid() + "的时效为:" + serviceEffectiveness.getTimeCost());
            } else {
                obj.setTime(new BigDecimal(Integer.MAX_VALUE));
                calcObject.putMsg("获取到服务：" + fixedLeftService.getServiceUid() + "时效为空，以极大的数计算时效权重：" + Integer.MAX_VALUE);
            }
            //时效权重
            if (fixedLeftService.getAgingWeighting() != null) {
                calcObject.putMsg("设置的时效权重：" + fixedLeftService.getAgingWeighting().doubleValue());
                BigDecimal fixedAgingWeight = new BigDecimal(fixedLeftService.getAgingWeighting());
                obj.setTimeCalcWeight(fixedAgingWeight.multiply(obj.getTime()));
                calcObject.putMsg("计算后的时效权重(时效*时效权重)：" + obj.getTimeCalcWeight().doubleValue());
            } else {
                obj.setTimeCalcWeight(BigDecimal.ZERO);
            }
            compares.add(obj);

        });

        //Fore xample
        //A服务：1.2元 15小时
        //B服务：1.5元 25小时
        //C服务：2.5元 20小时

        //价格排序 按价格升序排 价格低的在前面 like: 1.2元(1)  1.5元(2) 2.0元(3)
        compares.sort((item1, item2) -> item1.getPriceCalcWeight().compareTo(item2.getPriceCalcWeight()));
        for (int index = 0; index < compares.size(); index++) {
            compares.get(index).setPriceOrder(index);
        }

        calcObject.putMsg("价格排序后，服务的顺序：" + getServiceUids(compares));

        //时效排序 按时效升序排 时效快的在前面 like: 15小时(1)  20小时(2) 25小时(3)
        compares.sort((item1, item2) -> item1.getTimeCalcWeight().compareTo(item2.getTimeCalcWeight()));
        for (int index = 0; index < compares.size(); index++) {
            compares.get(index).setTimeOrder(index);
        }
        calcObject.putMsg("时效排序后，服务的顺序：" + getServiceUids(compares));

        //上面两个排序的和再排序        出现
        compares.forEach(item -> item.setCalcOrder(item.getPriceOrder() + item.getTimeOrder()));
        //A服务：1.2元(1) 15小时(1)   合计：2
        //B服务：1.5元(2) 25(3)      合计：5
        //C服务：2.5元(3) 20小时(2)   合计：5
        compares.sort((item1, item2) -> item1.getCalcOrder() - item2.getCalcOrder());

        calcObject.putMsg("综合排序后，服务的顺序：" + getServiceUids(compares));

        //只取排序第一的即为价格低时效快的服务
        calcObject.putMsg("择优匹配到的服务：" + compares.get(0).getService().getServiceUid());
        return compares.get(0).getService();
    }


    private String getServiceUids(List<WellCompareObj> compares) {

        if (CollectionUtils.isEmpty(compares)) {
            return "";
        }
        List<ProductServiceRel> inner = new ArrayList<>();
        compares.forEach(item -> inner.add(item.getService()));
        List<String> uids = EntityUtils.getStringPropListFromBeans(inner, "serviceUid");
        return StringUtils.toCommaString(uids);
    }


    /**
     * address 四级地址 2/3-国家-省-城市-区-街道  【6个】
     */
    private boolean matchAddress(String productServiceSettings, String address) {
        String[] addressSettingDetails = productServiceSettings.split(ADDRESSSPLIT);
        String[] addressDetails = address.split("-");
        for (String addressSettingDetail : addressSettingDetails) {
            //国家
            if (addressSettingDetail.startsWith("1") && addressSettingDetail.contains(addressDetails[1])) {
                return true;
            }
            //国内
            if (addressSettingDetail.startsWith("2")) {
                //省
                if (addressSettingDetail.contains(addressDetails[2])) {
                    return true;
                }
                //市
                if (addressSettingDetail.contains(addressDetails[3])) {
                    return true;
                }
                //区
                if (addressSettingDetail.contains(addressDetails[4])) {
                    return true;
                }
                //街道
                if (addressSettingDetail.contains(addressDetails[5])) {
                    return true;
                }
            }

            if (addressSettingDetail.startsWith("3")) {
                //国
                if (addressSettingDetail.contains(addressDetails[1])) {
                    return true;
                }
                //城市
                if (addressSettingDetail.contains(addressDetails[3])) {
                    return true;
                }
            }

        }
        return false;
    }


    private boolean hasAirClass(String serviceUid, String classIdsStr, OrdServiceAssignCalcDto calcDto) {
        if (StringUtils.isEmpty(classIdsStr)) {
            putMsg(calcDto, "开始匹配空运仓位,产品服务中设置的班次范围为空，跳过");
            return true;
        }
        putMsg(calcDto, "开始匹配空运仓位,产品服务中设置的班次范围:" + classIdsStr);
        String[] classIds = classIdsStr.split(ADDRESSSPLIT);

        for (String classId : classIds) {
            Flight flight = flightService.findSimpleById(Integer.parseInt(classId));
            if (flight != null) {
                putMsg(calcDto, "获取到航班,id:" + flight.getId() + ",航班号:" + flight.getCode());

                Map<String, Object> map = new HashMap<>(16);
                map.put("type", "AIR");
                map.put("classNo", flight.getCode());
                Date today = null;
                try {
                    today = DateUtils.getDayBegin(new Date());
                } catch (ParseException e) {
                    putMsg(calcDto, "获取日期异常");
                }
                map.put("startTime", today);
                List<SynClassCargo> classes = synClassCargoService.findByTypeClassAndDate(map);
                if (!CollectionUtils.isEmpty(classes)) {
                    SynClassCargo scc = classes.get(0);
                    putMsg(calcDto, "使用type:AIR,classId:" + flight.getCode() +
                            ",startTime" + DateUtils.formatSimpleDate(today) +
                            "获取到航班的仓位信息:剩余" + scc.getCargo().doubleValue() + scc.getCargoUnit());

                    if (scc.getCargo().compareTo(calcDto.getCusWeight()) >= 0) {
                        calcDto.getClassMap().put(serviceUid, flight.getId());
                        putMsg(calcDto, "匹配到航班:" + scc.getClassNo() + ",还剩余仓位:" + scc.getCargo().doubleValue());
                        break;
                    } else {
                        putMsg(calcDto, "仓位不足，过滤掉航班:" + scc.getCargo().doubleValue() + scc.getCargoUnit());
                    }
                } else {
                    putMsg(calcDto, "没有获取到航班当天的仓位信息，过滤掉航班:" + flight.getCode());
                }
            }
        }

        return true;
    }

    private boolean hasShipClass(String serviceUid, String classIdsStr, OrdServiceAssignCalcDto calcDto) {
        if (StringUtils.isEmpty(classIdsStr)) {
            putMsg(calcDto, "开始匹配海运仓位,产品服务中设置的班次范围为空，跳过");
            return true;
        }
        putMsg(calcDto, "开始匹配船运仓位,产品服务中设置的班次范围:" + classIdsStr);
        String[] classIds = classIdsStr.split(ADDRESSSPLIT);
        for (String classId : classIds) {
            ShippingLineSetting shippingLineSetting = shippingLineSettingService.findSimpleById(Integer.parseInt(classId));
            if (shippingLineSetting != null) {
                putMsg(calcDto, "获取到船班,id:" + shippingLineSetting.getId() + ",班号:" + shippingLineSetting.getCode());

                Map<String, Object> map = new HashMap<>(16);
                map.put("type", "SHIP");
                map.put("classNo", shippingLineSetting.getCode());
                Date today = null;
                try {
                    today = DateUtils.getDayBegin(new Date());
                } catch (ParseException e) {
                    putMsg(calcDto, "获取日期异常");
                }
                map.put("startTime", today);
                List<SynClassCargo> classes = synClassCargoService.findByTypeClassAndDate(map);
                if (!CollectionUtils.isEmpty(classes)) {
                    SynClassCargo scc = classes.get(0);
                    putMsg(calcDto, "使用type:SHIP,classId:" + shippingLineSetting.getCode() +
                            ",startTime" + DateUtils.formatSimpleDate(today) +
                            "获取到船班的仓位信息:" + scc.getCargo().doubleValue() + scc.getCargoUnit());
                    if (scc.getCargo().compareTo(calcDto.getCusWeight()) >= 0) {
                        calcDto.getClassMap().put(serviceUid, shippingLineSetting.getId());
                        putMsg(calcDto, "匹配到船班:" + scc.getClassNo());
                        break;
                    } else {
                        putMsg(calcDto, "仓位不足，过滤掉船班:" + scc.getCargo().doubleValue() + scc.getCargoUnit());
                    }
                }
            }
        }

        return true;
    }

    private void putMsg(OrdServiceAssignCalcDto calcDto, String msg) {
        calcDto.getCalcMsg().append("\r\n").append(msg);
    }

    private boolean isOptionService(ProductServicePropertyEnum psp) {
        return ProductServicePropertyEnum.OPTIONAL.getCode().equals(psp.getCode());
    }

    @Override
    public OrdServiceAssign findOkAssignByOrderNo(String orderNo) {
        OrdServiceAssign serviceAssign = null;
        List<OrdServiceAssign> serviceAssignList = this.findByOrderNos(Collections.singletonList(orderNo), true);
        if (!CollectionUtils.isEmpty(serviceAssignList)) {
            if (serviceAssignList.size() > 1) {
                throw Exceptions.bizException(ErrorCodes.ERROR_COMMON, "同服务类型多服务的分派表查询出多条数据！应该只有一条");
            }
            serviceAssign = serviceAssignList.get(0);
        }
        return serviceAssign;
    }


    @Override
    public OrdServiceAssign findByOrderNo(String orderNo) {
        OrdServiceAssign ordServiceAssign = null;
        List<OrdServiceAssign> ordServiceAssignList = new ArrayList<>();
        if (!StringUtils.isEmpty(orderNo)) {
            ordServiceAssignList = this.findByOrderNos(Collections.singletonList(orderNo), null);
        }
        if (!CollectionUtils.isEmpty(ordServiceAssignList)) {
            ordServiceAssign = ordServiceAssignList.get(0);
        }
        return ordServiceAssign;
    }

    /**
     * 根据订单号、服务的uid来获取下一个服务是哪个
     *
     * @param orderNo    订单号
     * @param serviceUid 上个服务的uid
     * @return 获取到的服务信息，如果没有获取到，则返回null
     */
    @Override
    public OrdOrderOptionService findNextServices(String orderNo, String serviceUid) {
        OrdOrderOptionService orderOptionService = null;
        if (!StringUtils.isEmpty(orderNo)) {
            List<OrdOrderOptionService> orderOptionServiceList = this.findOrderServiceByOrderNo(orderNo, null);
            for (int i = 0; i < orderOptionServiceList.size(); i++) {
                OrdOrderOptionService optionService = orderOptionServiceList.get(i);
                if (serviceUid.equals(optionService.getServiceUid())) {
                    i = i + 1;
                    if (i < orderOptionServiceList.size()) {
                        orderOptionService = orderOptionServiceList.get(i);
                    }
                }
            }
        }
        return orderOptionService;
    }


    /**
     * 根据订单号批量查询 订单中同服务类型多服务的分派表
     *
     * @param orderNoList 运单编号集合
     * @param isAssignOk  查询是否分派成功的
     * @return 数据集合
     */
    @Override
    public List<OrdServiceAssign> findByOrderNos(List<String> orderNoList, Boolean isAssignOk) {
        if (orderNoList == null || orderNoList.size() <= 0) {
            throw Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, orderNoList);
        }
        ParamData pd = new ParamData();
        pd.put("orderNos", orderNoList);
        pd.put("isAssignOk", isAssignOk);
        return dao.findForList("OrdServiceAssignMapper.findByOrderNos", pd, OrdServiceAssign.class);
    }


    @Override
    public OrdServiceAssign findByOrderNosWithoutAssign(String orderNo) {
        ParamData pd = new ParamData();
        pd.put("orderNo", orderNo);
        return (OrdServiceAssign) dao.findForObject("OrdServiceAssignMapper.findByOrderNosWithoutAssign", pd);
    }


    @Override
    public Map<String, OrdServiceAssign> findMapByOrderNos(List<String> orderNoList) {
        List<OrdServiceAssign> list = this.findByOrderNos(orderNoList, true);
        Map<String, OrdServiceAssign> serviceAssignMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(ordServiceAssign -> serviceAssignMap.put(ordServiceAssign.getOrderNo(), ordServiceAssign));
        }

        return serviceAssignMap;
    }

    /**
     * 根据订单号获取指定订单最新的分派信息，无论结果成功与否
     *
     * @param orderNo 订单号
     * @return 无论是不计费成功，只要有数据就返回。
     */
    @Override
    public OrdOrderOptionService getFirstServiceInfoByOrderNo(String orderNo) {
        OrdOrderOptionService optionService = null;
        List<OrdOrderOptionService> orderOptionServiceList = this.findOrderServiceByOrderNo(orderNo, null);
        if (!CollectionUtils.isEmpty(orderOptionServiceList)) {
            optionService = orderOptionServiceList.get(0);
        }
        return optionService;

    }
    @Override
    public List<OrdOrderOptionService> findOrderServiceByOrderNo(String orderNo){
       return this.findOrderServiceByOrderNo(orderNo,null);
    }
    @Override
    public List<OrdOrderOptionService> findOrderServiceByOrderNo(String orderNo, Boolean isAssignOk) {
        List<OrdOrderOptionService> optionServiceList = new ArrayList<>();
        List<OrdServiceAssign> ordServiceAssignList = this.findByOrderNos(Collections.singletonList(orderNo), isAssignOk);
        OrdServiceAssign serviceAssign = null;
        if (!CollectionUtils.isEmpty(ordServiceAssignList)) {
            serviceAssign = ordServiceAssignList.get(0);
        }
        if (!ObjectUtils.isEmpty(serviceAssign)) {

            List<String> serviceTypeCodeList = new ArrayList<>();
            if (!ObjectUtils.isEmpty(serviceAssign)) {
                Class<OrdServiceAssign> userCla = (Class<OrdServiceAssign>) serviceAssign.getClass();

                String stTypes = serviceAssign.getStTypes();

                if (!StringUtils.isEmpty(stTypes)) {
                    String[] stType = stTypes.split(ADDRESSSPLIT);
                    if (stType.length == 2) {
                        String stTypeOption = stType[1];
                        if (!StringUtils.isEmpty(stTypeOption)) {
                            serviceTypeCodeList = Arrays.asList(stTypeOption.split(","));
                        }
                    }


                    stTypes = stType[0];


                    stType = stTypes.split(",");
                    for (String type : stType) {
                        OrdOrderOptionService optionService = new OrdOrderOptionService();
                        String stName = type.substring(0, 5).toUpperCase();
                        optionService.setServiceTypeCode(stName);
                        List<String> propertyTypeCode = OrdOrderServiceImpl.extractMessageByRegular(type);
                        optionService.setServicePropertyCode(propertyTypeCode.get(0));


                        Field[] fields = userCla.getDeclaredFields();
                        for (Field field : fields) {
                            //设置些属性是可以访问的
                            field.setAccessible(true);
                            if (field.getName().toLowerCase().equals((optionService.getServiceTypeCode() + "id").toLowerCase())) {
                                //服务的id
                                try {
                                    if (field.get(serviceAssign) != null) {
                                        optionService.setServiceId((int) field.get(serviceAssign));
                                    }

                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            } else if (field.getName().toLowerCase().equals((optionService.getServiceTypeCode() + "Uid").toLowerCase())) {
                                //服务的uid
                                try {
                                    if (field.get(serviceAssign) != null) {
                                        optionService.setServiceUid((String) field.get(serviceAssign));
                                    }

                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            } else if (field.getName().toLowerCase().equals((optionService.getServiceTypeCode() + "ChannelNo").toLowerCase())) {
                                //服务的渠道单号ChannelNo
                                try {
                                    if (field.get(serviceAssign) != null) {
                                        optionService.setServiceChannelNo((String) field.get(serviceAssign));
                                    }

                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                            field.setAccessible(false);
                        }


                        if (ProductServicePropertyEnum.MAIN.getCode().equals(optionService.getServicePropertyCode())
                                || ProductServicePropertyEnum.REQUIRED.getCode().equals(optionService.getServicePropertyCode())
                                || serviceTypeCodeList.contains(optionService.getServiceTypeCode())) {
                            if (optionService.getServiceId() == 0 || StringUtils.isEmpty(optionService.getServiceUid())) {
                                optionService.setAssignSign(false);
                            }
                            optionService.setOrderNo(serviceAssign.getOrderNo());
                            optionServiceList.add(optionService);
                        }

                    }
                }
            }


        }


        return optionServiceList;
    }

    @Override
    public void insert(OrdServiceAssign ordServiceAssign) {
        dao.delete("OrdServiceAssignMapper.deleteByOrderNo", ordServiceAssign.getOrderNo());
        dao.save("OrdServiceAssignMapper.insert", ordServiceAssign);
    }


}


class WellCompareObj {
    /**
     * 实际的价格钱数
     */
    private BigDecimal price;
    /**
     * 价格权重
     */
    private BigDecimal priceCalcWeight;
    /**
     * 价格顺序
     */
    private int priceOrder;

    /**
     * 实际的时间小时数
     */
    private BigDecimal time;

    /**
     * 实际的时间权重
     */
    private BigDecimal timeCalcWeight;
    /**
     * 时间顺序
     */
    private int timeOrder;

    private ProductServiceRel service;

    private int calcOrder;

    WellCompareObj(ProductServiceRel service) {
        this.service = service;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    BigDecimal getPriceCalcWeight() {
        return priceCalcWeight;
    }

    void setPriceCalcWeight(BigDecimal priceCalcWeight) {
        this.priceCalcWeight = priceCalcWeight;
    }

    int getPriceOrder() {
        return priceOrder;
    }

    void setPriceOrder(int priceOrder) {
        this.priceOrder = priceOrder;
    }

    public BigDecimal getTime() {
        return time;
    }

    public void setTime(BigDecimal time) {
        this.time = time;
    }

    BigDecimal getTimeCalcWeight() {
        return timeCalcWeight;
    }

    void setTimeCalcWeight(BigDecimal timeCalcWeight) {
        this.timeCalcWeight = timeCalcWeight;
    }

    int getTimeOrder() {
        return timeOrder;
    }

    void setTimeOrder(int timeOrder) {
        this.timeOrder = timeOrder;
    }

    public ProductServiceRel getService() {
        return service;
    }

    public void setService(ProductServiceRel service) {
        this.service = service;
    }

    int getCalcOrder() {
        return calcOrder;
    }

    void setCalcOrder(int calcOrder) {
        this.calcOrder = calcOrder;
    }
}


